package org.textreport.lang;

import java.util.Iterator;
import java.util.Map;

import org.textreport.core.ExecutionContext;
import org.textreport.core.SQLType;
import org.textreport.core.SQLUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class LoopExpression extends ExpressionList {
	private String initExp;
	private String itemName;
	private int sourceLine;
	private boolean hasMore;

	public LoopExpression(String onVar, String itemName,Expression parent) {
		super(parent);
		this.initExp = onVar;
		this.itemName = itemName;
	}

	@Override
	public void eval(ExecutionContext ctx) {
		try {

			Object list = null;

			try {
				list = ctx.evalJavaScript(initExp);
			} catch (Exception e) {
				ctx.error("$loop()", this, "Erro ao executar inicializador.", e);
			}

			if (list instanceof NodeList) {
				NodeList nl = (NodeList) list;
				for (int i = 0; i < nl.getLength(); i++) {
					Node n = nl.item(i);
					ctx.putVariable(itemName, n);
					super.eval(ctx);
				}
				ctx.unset(itemName);
			} else if (list instanceof Number) {
				int max = ((Number) list).intValue();

				for (int i = 0; i < max; i++) {
					ctx.putVariable(itemName, i);
					super.eval(ctx);
				}
			} else if (list instanceof SQLType) {
				SQLType sql = (SQLType) list;

				ctx.putVariable(itemName, sql);

				if (SQLUtils.sqlOpen(sql, ctx)) {
					hasMore = false;

					do {
						Map<String, Object> thisRow = sql.getFieldValues();

						doFieldsInjection(ctx, thisRow, itemName);

						hasMore = sql.next();

						if(hasMore){
							doFieldsInjection(ctx, sql.getFieldValues(), itemName + "$next");
						}

						super.eval(ctx);
					} while (hasMore);
				}

				sql.close();
			}
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private void doFieldsInjection(ExecutionContext ctx, Map<String, Object> fields, String varName) throws Exception {
		StringBuilder buf = new StringBuilder();

		buf.append("var ").append(varName).append(" = {};");

		for (Iterator<Map.Entry<String, Object>> ite = fields.entrySet().iterator(); ite.hasNext();) {
			Map.Entry<String, Object> entry = ite.next();
			String name = entry.getKey();

			String internalName = varName + "_" + name;
			ctx.putVariable(internalName, entry.getValue());

			buf.append(varName).append("['").append(name).append("'] = ").append(internalName).append(";");

			if (ite.hasNext()) {
				buf.append("\n");
			}
		}

		ctx.evalJavaScript(buf.toString());

		for (Iterator<Map.Entry<String, Object>> ite = fields.entrySet().iterator(); ite.hasNext();) {
			Map.Entry<String, Object> entry = ite.next();
			String name = entry.getKey();

			String internalName = varName + "_" + name;
			ctx.unset(internalName);
		}
	}

	@Override
	public boolean hasBody() {
		return true;
	}

	@Override
	public String toString() {
		return "LoopExpression [onVar=" + initExp + ", itemName=" + itemName + "]";
	}

	@Override
	public int getSourceLine() {
		return sourceLine;
	}

	@Override
	public void setSourceLine(int line) {
		sourceLine = line;
	}

	public String getItemName() {
		return itemName;
	}

	public boolean hasMore() {
		return hasMore;
	}
}
